#include "StdAfx.h"
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include "TimeStampResponse.h"
#include "GemsecUtilities.h"
#include "errors.h"
#include "VerifyCPP.h"

#using < mscorlib.dll >
using namespace System;
using namespace System::IO;

namespace Safe {

	TimestampResponse::~TimestampResponse(void){
	}

	TimestampResponse::TimestampResponse(array<unsigned char>^ data, String^ hashOid, Safe::ManagedCryptBlob^ hash) 
	{
		// convert the data
		BYTE* bArr = new BYTE[ data->Length ];
		System::Runtime::InteropServices::Marshal::Copy( data, 0, (IntPtr)bArr, data->Length );
		// convert the OID
		System::IntPtr ptr = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(hashOid);
		const char *ansiOid = static_cast<const char*>(ptr.ToPointer());
		// convert the hash
		CRYPT_DER_BLOB hashBlob;
		hashBlob.cbData = hash->Length;
		hashBlob.pbData = new BYTE[ hashBlob.cbData ];
		hash->CreateBlob( hashBlob );

		try { 
			DecodeResponse( bArr, data->Length, ansiOid, &hashBlob );
		} finally { 
			System::Runtime::InteropServices::Marshal::FreeHGlobal(ptr);
			delete [] hashBlob.pbData;
			delete [] bArr;	
		}
	}

	
	TimestampResponse::TimestampResponse(BYTE * pData, DWORD cData, const char *hashOid, CRYPT_DER_BLOB *hash) {
		DecodeResponse( pData, cData, hashOid, hash );
	}



	///	This function decodes a timestamp response and validates the signer's certificate
	void TimestampResponse::DecodeResponse(BYTE * pData, DWORD cData, const char *hashOid, CRYPT_DER_BLOB *hash)
	{
		
		CRYPT_DATA_BLOB blobin, blobout;
		CRYPT_SEQUENCE_OF_ANY *TimeStampResp = NULL;
		CRYPT_SEQUENCE_OF_ANY *PKIStatusInfo = NULL;
		CRYPT_CONTENT_INFO *tstContent = NULL;
		HCRYPTMSG hMsg = NULL;
		DWORD *PKIStatus = NULL;
		BYTE *pTstContent = NULL;
		PCCERT_CONTEXT pSignerCert = NULL;
		HCERTCHAINENGINE         hChainEngine = NULL;
		PCCERT_CHAIN_CONTEXT     pChainContext = NULL;	

		try { 
			//X509_SEQUENCE_OF_ANY
			if (0 != CGemsecUtilities::CreateBlob(pData, cData, blobin)) {
				throw gcnew AsnException(BAD_PARAM);
			}

			if (0 != CGemsecUtilities::Decode(blobin, X509_SEQUENCE_OF_ANY, blobout)) {
				throw gcnew AsnException(DECODE_TIMESTAMPRESP_FAIL);			
			}

			//	TimeStampResp ::= SEQUENCE  {
			//		status                  PKIStatusInfo,
			//		timeStampToken          TimeStampToken     OPTIONAL  }
			CGemsecUtilities::Copy(blobout, (void **)&TimeStampResp);
			CGemsecUtilities::FreeMem(&blobout);

			// the status should always exist.  the token might not if the status is failure
			if ((0 == &TimeStampResp->cValue) || (NULL == &TimeStampResp->rgValue)) {
				throw gcnew AsnException(DECODE_TIMESTAMPRESP_FAIL);
			}

			if (0 != CGemsecUtilities::Decode(TimeStampResp->rgValue[0], X509_SEQUENCE_OF_ANY, blobout)) {
				throw gcnew AsnException(DECODE_PKISTATUSINFO_FAIL);
			}

			//	PKIStatusInfo ::= SEQUENCE {
			//		status        PKIStatus,
			//		statusString  PKIFreeText     OPTIONAL,
			//		failInfo      PKIFailureInfo  OPTIONAL  }
			CGemsecUtilities::Copy(blobout, (void **)&PKIStatusInfo);
			CGemsecUtilities::FreeMem(&blobout);

			// for now, only care about the PKIStatus.  It should always exist.
			if ((0 == &PKIStatusInfo->cValue) || (NULL == &PKIStatusInfo->rgValue)) {
				throw gcnew AsnException(DECODE_PKISTATUSINFO_FAIL);
			}

			if (0 != CGemsecUtilities::Decode(PKIStatusInfo->rgValue[0], X509_INTEGER, blobout)) {
				throw gcnew AsnException(DECODE_PKISTATUSINFO_FAIL);
			}

			//   PKIStatus ::= INTEGER {
			//	  granted                (0),
			//	  grantedWithMods        (1),
			//	  rejection              (2),
			//	  waiting                (3),
			//	  revocationWarning      (4),
			//	  revocationNotification (5)
			CGemsecUtilities::Copy(blobout, (void **)&PKIStatus);
			CGemsecUtilities::FreeMem(&blobout);

			if ((0 == *PKIStatus) || (1 == *PKIStatus))
			{
				// in these cases, there is a timestamp token to decode.
				if (2 != TimeStampResp->cValue) {
					throw gcnew AsnException(DECODE_TIMESTAMPRESP_FAIL);
				}

				//	TimeStampToken ::= ContentInfo
				//		-- contentType is id-signedData ([CMS])
				//		-- content is SignedData ([CMS])
				//	PKCS_CONTENT_INFO
				if (0 != CGemsecUtilities::Decode(TimeStampResp->rgValue[1], PKCS_CONTENT_INFO, blobout))
				{
					throw gcnew AsnException(DECODE_PKISTATUSINFO_FAIL);
				}
				
				CGemsecUtilities::Copy(blobout, (void **)&tstContent);
				CGemsecUtilities::FreeMem(&blobout);
				//should make sure tstContent->pszObjId is the right id
				CRYPT_VERIFY_MESSAGE_PARA cvmp;
				cvmp.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
				cvmp.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
				cvmp.hCryptProv = 0;
				cvmp.pfnGetSignerCertificate = NULL;
				cvmp.pvGetArg = NULL;
				blobout.cbData = 0;
				blobout.pbData = NULL;

				hMsg = CryptMsgOpenToDecode(MY_ENCODING_TYPE, 0, CMSG_SIGNED, NULL, NULL, NULL);
				BOOL b = CryptMsgUpdate(hMsg, tstContent->Content.pbData, tstContent->Content.cbData, TRUE);
				DWORD cTstContent = 0;
				b = CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &cTstContent);

				//d = GetLastError();
				if (0 == cTstContent) { 
					throw gcnew AsnException(ENCODE_SIZE_FAIL);
				}
				pTstContent = new BYTE[cTstContent];
				b = CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pTstContent, &cTstContent);
				if (!b) { 
					throw gcnew AsnException(DECODE_TIMESTAMPCONTENT_FAIL);
				}

				//pTstContent looks like this:
				//SEQUENCE {
				//  INTEGER 1
				//  OBJECT IDENTIFIER '1 2'
				//  SEQUENCE {
				//		SEQUENCE {
				//		  OBJECT IDENTIFIER sha1 (1 3 14 3 2 26)
				//		  NULL
				//		}
				//		OCTET STRING
				//		  01 02 03 04 05 10 20 30 40 50 60 70 80 90 A0 B0
				//		  C0 D0 E0 F0
				//	}
				//  INTEGER
				//		E1 64 50 80 18 E5 75 11
				//  GeneralizedTime '20070425192653Z'
				//  }
				DecodeTstContent(pTstContent, cTstContent, hashOid, hash);

				DWORD numCerts = 0;
				DWORD iCerts = sizeof(DWORD);
				b = CryptMsgGetParam(hMsg, CMSG_CERT_COUNT_PARAM, 0, &numCerts, &iCerts);
				BOOL foundSigner = 0;
				iCerts = 0;

				// iterate through all the certs to find the signer
				pSignerCert = NULL;
				while (iCerts < numCerts && !foundSigner)
				{
					b = CryptMsgGetParam(hMsg, CMSG_CERT_PARAM, iCerts, blobout.pbData, &blobout.cbData);
					if (0 == blobout.cbData) { 
						throw gcnew AsnException(ENCODE_SIZE_FAIL);
					}
					blobout.pbData = new BYTE[blobout.cbData];
					b = CryptMsgGetParam(hMsg, CMSG_CERT_PARAM, iCerts, blobout.pbData, &blobout.cbData);
					if (!b) {
						throw gcnew AsnException(DECODE_TIMESTAMPSIGNER_FAIL);
					}

					iCerts++;

					//static BYTE cert[] = {0x30, 0x82, 0x05, 0xCB, 0x30, 0x82, 0x05, 0x34, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x07, 0x1A, 0x00, 0x01, 0x00, 0x06, 0x0B, 0x62, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x77, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0C, 0x30, 0x0A, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x03, 0x4A, 0x4E, 0x4A, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x1A, 0x4A, 0x4E, 0x4A, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x4B, 0x65, 0x79, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x31, 0x35, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2C, 0x4A, 0x4E, 0x4A, 0x20, 0x50, 0x72, 0x69, 0x6E, 0x63, 0x69, 0x70, 0x61, 0x6C, 0x20, 0x4F, 0x6E, 0x4C, 0x69, 0x6E, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1E, 0x17, 0x0D, 0x30, 0x37, 0x30, 0x34, 0x30, 0x32, 0x31, 0x37, 0x33, 0x34, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x30, 0x37, 0x30, 0x35, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x72, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0C, 0x30, 0x0A, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x03, 0x4A, 0x4E, 0x4A, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x1A, 0x4A, 0x4E, 0x4A, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x4B, 0x65, 0x79, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x69, 0x65, 0x73, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x0F, 0x4F, 0x43, 0x53, 0x50, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x64, 0x65, 0x72, 0x73, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0D, 0x4E, 0x43, 0x53, 0x55, 0x53, 0x52, 0x41, 0x4F, 0x43, 0x53, 0x50, 0x56, 0x31, 0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0x96, 0xFF, 0xBB, 0xAB, 0x93, 0xD2, 0xF7, 0x96, 0xEB, 0x44, 0x76, 0x7B, 0x49, 0xA2, 0x0F, 0x2E, 0xD1, 0x4C, 0xF5, 0x7F, 0x77, 0x9C, 0xCE, 0x8B, 0xD4, 0xBC, 0xC5, 0xFC, 0x14, 0xCC, 0x05, 0xF2, 0xAF, 0x68, 0xCC, 0x91, 0xE7, 0x3E, 0x35, 0xDE, 0x23, 0xFC, 0x25, 0xAD, 0xB6, 0x7F, 0xBA, 0x01, 0x50, 0x38, 0xF2, 0x92, 0xE4, 0x27, 0x20, 0x3E, 0xA1, 0x3B, 0x12, 0x89, 0x34, 0xF6, 0x88, 0x82, 0x0E, 0xFD, 0x7A, 0xBC, 0xF4, 0xB8, 0x8D, 0x3C, 0x5B, 0xD1, 0xCA, 0xB3, 0x64, 0xDF, 0x7C, 0x9C, 0xCE, 0xCF, 0x00, 0xFA, 0x58, 0xB8, 0x06, 0x14, 0x15, 0xA1, 0xAE, 0x64, 0xEA, 0xFB, 0xFD, 0xE4, 0x5D, 0xE7, 0xB1, 0xC2, 0x73, 0x95, 0x4D, 0xBD, 0x87, 0x3D, 0x79, 0x12, 0x52, 0x25, 0xAF, 0xD7, 0x7B, 0x03, 0x10, 0xDF, 0xCC, 0x44, 0xC8, 0x1F, 0x5A, 0x82, 0x44, 0x4E, 0x66, 0x53, 0xA5, 0xA9, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x82, 0x03, 0x64, 0x30, 0x82, 0x03, 0x60, 0x30, 0x16, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x01, 0x01, 0xFF, 0x04, 0x0C, 0x30, 0x0A, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x30, 0x0F, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x04, 0x02, 0x05, 0x00, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0x81, 0xE0, 0x10, 0xE8, 0xCB, 0x7A, 0x1C, 0xB1, 0xBE, 0x50, 0x3F, 0xBC, 0xC2, 0xCE, 0x10, 0x93, 0x68, 0x9E, 0xC2, 0x05, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xEB, 0x3F, 0x72, 0xD3, 0xA6, 0x15, 0x51, 0x40, 0xEE, 0x39, 0x00, 0x18, 0xBA, 0x4C, 0xF2, 0x6E, 0xCD, 0x4F, 0xAE, 0xC6, 0x30, 0x3E, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x32, 0x30, 0x30, 0x30, 0x2E, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6A, 0x6A, 0x65, 0x64, 0x73, 0x64, 0x61, 0x74, 0x61, 0x2E, 0x6A, 0x6E, 0x6A, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x70, 0x6F, 0x6C, 0x63, 0x61, 0x2E, 0x70, 0x37, 0x63, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x06, 0xC0, 0x30, 0x82, 0x02, 0x7D, 0x06, 0x03, 0x55, 0x1D, 0x20, 0x04, 0x82, 0x02, 0x74, 0x30, 0x82, 0x02, 0x70, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x01, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x02, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x03, 0x30, 0x81, 0xBA, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x04, 0x30, 0x81, 0xAA, 0x30, 0x81, 0xA7, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x81, 0x9A, 0x1A, 0x81, 0x97, 0x49, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x4F, 0x43, 0x53, 0x50, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x61, 0x20, 0x53, 0x41, 0x46, 0x45, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x62, 0x6C, 0x65, 0x20, 0x72, 0x75, 0x6C, 0x65, 0x73, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x62, 0x65, 0x20, 0x66, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x41, 0x46, 0x45, 0x20, 0x43, 0x50, 0x20, 0x72, 0x65, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x6F, 0x72, 0x79, 0x20, 0x61, 0x74, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x73, 0x61, 0x66, 0x65, 0x2D, 0x62, 0x69, 0x6F, 0x70, 0x68, 0x61, 0x72, 0x6D, 0x61, 0x2E, 0x6F, 0x72, 0x67, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x05, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x07, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x08, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x09, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x0A, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x0B, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x0C, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x0D, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x0E, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x0F, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x10, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x11, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x12, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x13, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x14, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x15, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x16, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x17, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x18, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x19, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x1A, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x1B, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x1C, 0x30, 0x0D, 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x02, 0x03, 0x02, 0x01, 0x1D, 0x30, 0x24, 0x06, 0x03, 0x55, 0x1D, 0x11, 0x04, 0x1D, 0x30, 0x1B, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6A, 0x6A, 0x65, 0x64, 0x73, 0x6F, 0x63, 0x73, 0x70, 0x31, 0x2E, 0x6A, 0x6E, 0x6A, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x88, 0x45, 0x18, 0x4C, 0x66, 0x1B, 0xDE, 0x13, 0x3B, 0x01, 0x40, 0xE5, 0x29, 0xBA, 0x3E, 0x1B, 0x7D, 0xD0, 0x4B, 0xA9, 0xF4, 0x3F, 0x6E, 0x4A, 0xDC, 0x1E, 0x8E, 0x0F, 0xF2, 0x8C, 0xB2, 0xCD, 0x9F, 0x80, 0x09, 0x63, 0x4C, 0xB6, 0x60, 0xFD, 0x3E, 0xB7, 0x66, 0x1F, 0x16, 0x2B, 0x14, 0xC9, 0x6D, 0xD9, 0xD6, 0xDD, 0x7A, 0x24, 0xC0, 0xE9, 0x7D, 0x68, 0xC1, 0x1C, 0x96, 0x02, 0x91, 0x53, 0x65, 0x5F, 0x4D, 0xAF, 0x3A, 0x1A, 0x9B, 0xA8, 0x25, 0xCF, 0xFE, 0xCA, 0x89, 0x79, 0xDA, 0x9D, 0x79, 0x00, 0xF3, 0x38, 0x44, 0x0F, 0x20, 0x5B, 0x07, 0xEC, 0x83, 0x3C, 0x6A, 0x14, 0xEF, 0xC1, 0x27, 0x5B, 0xB3, 0x17, 0x6D, 0x9F, 0xB0, 0x3F, 0x82, 0xF5, 0xBE, 0x9E, 0x54, 0xB4, 0x00, 0xD2, 0xE2, 0xAA, 0x89, 0x7B, 0xF9, 0x7F, 0x1A, 0xB1, 0x90, 0x73, 0x56, 0x2D, 0x49, 0x6A, 0xE0, 0x98};
					//pSignerCert = CertCreateCertificateContext(MY_ENCODING_TYPE, cert, 1487);
					pSignerCert = CertCreateCertificateContext(MY_ENCODING_TYPE, blobout.pbData, blobout.cbData);
					if (NULL == pSignerCert) {
						throw gcnew AsnException(DECODE_TIMESTAMPSIGNER_FAIL);
					}
					CGemsecUtilities::FreeMem(&blobout);

					// looking for cert with EKU of id-pk-timeStamping {1.3.6.1.5.5.7.3.8)
					PCERT_ENHKEY_USAGE pEku = NULL;
					DWORD cEku = 0;

					b = CertGetEnhancedKeyUsage(pSignerCert, 
						CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pEku, &cEku);

					if (0 == cEku)  {
						CertFreeCertificateContext(pSignerCert);
						pSignerCert = NULL;
					} else { 
						pEku = (PCERT_ENHKEY_USAGE)new BYTE[cEku];

						b = CertGetEnhancedKeyUsage(pSignerCert, 
							CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pEku, &cEku);

						if (!b) {
							CertFreeCertificateContext(pSignerCert);
							delete[] pEku;
							pEku = NULL;
							pSignerCert = NULL;
						} else { 
							DWORD iEku=0;
							for (iEku=0; iEku<pEku->cUsageIdentifier; iEku++)
							{
								
								if (0==strcmp(szOID_PKIX_KP_TIMESTAMP_SIGNING,pEku->rgpszUsageIdentifier[iEku]))
								{
									foundSigner=TRUE;
									delete[] pEku;
									pEku = NULL;
									break;
								}
							}
						}
					}
				}

				if( pSignerCert == NULL ) {
					throw gcnew Safe::ChainBuildException("Timestamp signer certificate not included in response");
				}

				// verify the signer's cert
				CERT_CHAIN_PARA          ChainPara;

				memset(&ChainPara, 0, sizeof(CERT_CHAIN_PARA));
				ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
				
				if(!(CertGetCertificateChain(
							NULL, 
							pSignerCert,
							NULL, 
							NULL, 
							&ChainPara, 
							CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, 
							NULL, 
							&pChainContext))) {
						throw gcnew Safe::ChainBuildException("Failed to build a certificate chain for the timestamp certificate");
				}

				DWORD chainStatus = pChainContext->TrustStatus.dwErrorStatus;
				chainStatus &= REMOVE_NONSTANDARD_ERRORS_MASK;
				if(	CERT_TRUST_NO_ERROR == chainStatus ) {

					// path is good
					CERT_CHAIN_POLICY_PARA policyPara;
					CERT_CHAIN_POLICY_STATUS policyStatus;
					memset(&policyPara, 0, sizeof(CERT_CHAIN_POLICY_PARA));
					memset(&policyStatus, 0, sizeof(CERT_CHAIN_POLICY_STATUS));

					policyPara.cbSize = sizeof(CERT_CHAIN_POLICY_PARA);			
					policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS);

					// Flags indicating potential non-valid conditions to be ignored 
					policyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;

					if(!(CertVerifyCertificateChainPolicy(  
							CERT_CHAIN_POLICY_BASIC_CONSTRAINTS,
							pChainContext, 
							&policyPara, 
							&policyStatus)))
					{
						policyStatus.dwError = ::GetLastError();
					}
					if (0 != policyStatus.dwError)
					{
						throw gcnew TimestampException("The timestamp server's certificate failed the certificate policy check");
					}

				}
				else
				{
					// path is bad
					Safe::ChainBuildException^ exc =  
						gcnew Safe::ChainBuildException("The timestamp server's certificate is invalid");
					exc->Detail = Safe::CertificateValidation::PathBuildErrorToString(chainStatus);
					throw exc;
				}
			}
			else {
				throw gcnew Safe::TimestampException("The server did not issue a timestamp in the response");
			}
		} finally { 

			/*
			Declarations requiring release:
			CRYPT_DATA_BLOB blobin, blobout;
			CRYPT_SEQUENCE_OF_ANY *TimeStampResp = NULL;
			CRYPT_SEQUENCE_OF_ANY *PKIStatusInfo = NULL;
			CRYPT_CONTENT_INFO *tstContent = NULL;
			HCRYPTMSG hMsg = NULL;
			DWORD *PKIStatus = NULL;
			BYTE *pTstContent = NULL;
			PCCERT_CONTEXT pSignerCert = NULL;
			HCERTCHAINENGINE         hChainEngine = NULL;
			PCCERT_CHAIN_CONTEXT     pChainContext = NULL;
			*/

			CGemsecUtilities::FreeMem(&blobin);
			CGemsecUtilities::FreeMem(&blobout);
			CGemsecUtilities::FreeVoid((void **)&TimeStampResp);
			CGemsecUtilities::FreeVoid((void **)&PKIStatusInfo);
			CGemsecUtilities::FreeVoid((void **)&PKIStatus);
			if( pTstContent != NULL ) { 
				delete [] pTstContent;
			}
			if( pSignerCert != NULL ) { 
				CertFreeCertificateContext(pSignerCert);
				pSignerCert = NULL;
			}
			if( hChainEngine != NULL ) { 
				CertFreeCertificateChainEngine(hChainEngine);
			}
			if( pChainContext != NULL ) { 
				CertFreeCertificateChain(pChainContext);
			}
			if( hMsg != NULL ) { 
				CryptMsgClose(hMsg);
			}
		}
	}



	// decodes the time stamp token content object
	void TimestampResponse::DecodeTstContent(BYTE * pContent, DWORD cContent, const char *hashOid, CRYPT_DER_BLOB *hash)
	{
		CRYPT_DATA_BLOB blobin, blobout;
		CRYPT_SEQUENCE_OF_ANY *TSTInfo = NULL;
		DWORD *version = NULL;
		CRYPT_SEQUENCE_OF_ANY *MessageImprint = NULL;
		CRYPT_DER_BLOB *ImprintHash = NULL;
		FILETIME *genTime = NULL;
#ifndef NOTREALLYVISTA
		CRYPT_ALGORITHM_IDENTIFIER *ImprintHashAlgorithm = NULL;
#endif

		try {
			if (0 != CGemsecUtilities::CreateBlob(pContent, cContent, blobin)) {
				throw gcnew AsnException(BAD_PARAM);
			}

			if (0 != CGemsecUtilities::Decode(blobin, X509_SEQUENCE_OF_ANY, blobout))
			{
				throw gcnew AsnException(DECODE_TSTCONTENT_FAIL);
			}

			//TSTInfo ::= SEQUENCE  {
			//   version                      INTEGER  { v1(1) },
			//   policy                       TSAPolicyId,
			//   messageImprint               MessageImprint,
			//	 -- MUST have the same value as the similar field in
			//	 -- TimeStampReq
			//   serialNumber                 INTEGER,
			//	-- Time-Stamping users MUST be ready to accommodate integers
			//	-- up to 160 bits.
			//   genTime                      GeneralizedTime,
			//   accuracy                     Accuracy                 OPTIONAL,
			//   ordering                     BOOLEAN             DEFAULT FALSE,
			//   nonce                        INTEGER                  OPTIONAL,
			//	 -- MUST be present if the similar field was present
			//	 -- in TimeStampReq.  In that case it MUST have the same value.
			//   tsa                          [0] GeneralName          OPTIONAL,
			//   extensions                   [1] IMPLICIT Extensions   OPTIONAL  }
			CGemsecUtilities::Copy(blobout, (void **)&TSTInfo);
			CGemsecUtilities::FreeMem(&blobout);

			// the status should always exist.  the token might not if the status is failure
			if ((5 > TSTInfo->cValue) || (NULL == &TSTInfo->rgValue)) {
				throw gcnew AsnException(DECODE_TSTCONTENT_FAIL);
			}

			if (0 != CGemsecUtilities::Decode(TSTInfo->rgValue[0], X509_INTEGER, blobout)) {
				throw gcnew AsnException(DECODE_TSTCONTENT_VER_FAIL);
			}

			
			CGemsecUtilities::Copy(blobout, (void **)&version);
			CGemsecUtilities::FreeMem(&blobout);

			if (1 != *version) {
				throw gcnew AsnException(DECODE_TSTCONTENT_VER_FAIL);
			}

			// ignoring TSAPolicyID
			if (0 != CGemsecUtilities::Decode(TSTInfo->rgValue[2], X509_SEQUENCE_OF_ANY, blobout)) {
				throw gcnew AsnException(DECODE_TSTCONTENT_IMPRINT_FAIL);
			}

			//	MessageImprint ::= SEQUENCE  {
			//		hashAlgorithm                AlgorithmIdentifier,
			//		hashedMessage                OCTET STRING  }
			CGemsecUtilities::Copy(blobout, (void **)&MessageImprint);
			CGemsecUtilities::FreeMem(&blobout);

			if (0 != CGemsecUtilities::Decode(MessageImprint->rgValue[0], X509_ALGORITHM_IDENTIFIER, blobout)) {
				/* won't work if this computer isn't vista */
#ifndef NOTREALLYVISTA
				throw gcnew AsnException(DECODE_TSTCONTENT_IMPRINT_ALGID_FAIL);
#endif
			}

#ifndef NOTREALLYVISTA
			
			CGemsecUtilities::Copy(blobout, (void **)&ImprintHashAlgorithm);
			CGemsecUtilities::FreeMem(&blobout);
			// check and see if ImprintHashAlgorithm->pszObjId is right 
			if (0 != strcmp(hashOid, ImprintHashAlgorithm->pszObjId)) {
				throw gcnew AsnException(DECODE_TSTCONTENT_IMPRINT_HASH_WRONGALG);
			}
#endif

			if (0 != CGemsecUtilities::Decode(MessageImprint->rgValue[1], X509_OCTET_STRING, blobout)) {
				throw gcnew AsnException(DECODE_TSTCONTENT_IMPRINT_HASH_FAIL);
			}

			
			CGemsecUtilities::Copy(blobout, (void **)&ImprintHash);
			CGemsecUtilities::FreeMem(&blobout);
			// check and see if the hash is right
			if (!CGemsecUtilities::BlobsAreEqual(ImprintHash, hash)) {
				throw gcnew TimestampException("The hash value in the timestamp is incorrect");
			}

			// ignoring serialNumber
			if (0 != CGemsecUtilities::Decode(TSTInfo->rgValue[4], X509_CHOICE_OF_TIME, blobout)) {
				throw gcnew AsnException(DECODE_TSTCONTENT_TIMESTAMP_FAIL);
			}

			
			CGemsecUtilities::Copy(blobout, (void **)&genTime);
			CGemsecUtilities::FreeMem(&blobout);		
			m_timestampTime = DateTime::FromFileTimeUtc(
				(((__int64)genTime->dwHighDateTime) << 32) | (genTime->dwLowDateTime & 0xFFFFFFFF)
				);

		} finally { 

			//cleanup
			/*
			CRYPT_DATA_BLOB blobin, blobout;
			CRYPT_SEQUENCE_OF_ANY *TSTInfo = NULL;
			DWORD *version = NULL;
			CRYPT_SEQUENCE_OF_ANY *MessageImprint = NULL;
			CRYPT_DER_BLOB *ImprintHash = NULL;
			FILETIME *genTime = NULL;
	#ifndef NOTREALLYVISTA
			CRYPT_ALGORITHM_IDENTIFIER *ImprintHashAlgorithm = NULL;
	#endif
			*/
			CGemsecUtilities::FreeMem(&blobin);
			CGemsecUtilities::FreeMem(&blobout);
			CGemsecUtilities::FreeVoid((void**)&TSTInfo);
			CGemsecUtilities::FreeVoid((void**)&version);
			CGemsecUtilities::FreeVoid((void**)&MessageImprint);
			CGemsecUtilities::FreeVoid((void**)&ImprintHash);
			CGemsecUtilities::FreeVoid((void**)&genTime);
		#ifndef NOTREALLYVISTA
			CGemsecUtilities::FreeVoid((void**)&ImprintHashAlgorithm);
		#endif
		}	
	}
}